
#include <BScene>
#include <BNode>
#include <BMesh>
#include <BCube>
#include <BCone>
#include <BTube>
#include <BTorus>
#include <BCapsule>
#include <BTextual>
#include <BBillboard>
#include <BGridPlane>

#include <BPickHit>
#include <BPicker>
#include "member_BGroup.h"
#include "member_BNode.h"
#include "member_BShape.h"
#include "member_BElement.h"
#include "member_BPicker.h"
#include "member_BPickHit.h"
#include "member_BBillboard.h"

using namespace BWE;

#define member					(*(member_BPicker*)_ptr)
#define member_allocate()		_ptr = new member_BPicker(this)
#define member_release()		delete (member_BPicker*)_ptr

BPicker::BPicker()
{
	member_allocate();
}
BPicker::BPicker(const BLine& line)
{
	member_allocate();
	this->setLine(line);
}
BPicker::BPicker(const BVector& begin, const BVector& end)
{
	member_allocate();
	this->setLine(begin, end);
}
BPicker::~BPicker()
{
	member_release();
}

void BPicker::setHitNums(int hitNums)
{
	if (hitNums < 1)
		hitNums = 1;
	if (member.hitNums != hitNums)
	{
		member.hitNums = hitNums;
	}
}
int BPicker::hitNums() const
{
	return member.hitNums;
}

void BPicker::setLine(const BVector& begin, const BVector& end)
{
	member.line.set(begin, end);
	member.group = 0;
	member.node = 0;
	member.pickHits.clear();
}
void BPicker::setLine(const BLine& line)
{
	member.line = line;
	member.group = 0;
	member.node = 0;
	member.pickHits.clear();
}
const BLine& BPicker::line() const
{
	return member.line;
}

bool BPicker::exclude(const BObject* object)
{
	if (object && !member.exclusions.contain(object))
	{
		int index = member.exclusions.insert(object);
		return member.exclusions.check(index);
	}
	return false;
}
bool BPicker::include(const BObject* object)
{
	if (object && !member.inclusions.contain(object))
	{
		int index = member.inclusions.insert(object);
		return member.inclusions.check(index);
	}
	return false;
}

int BPicker::exclusionCount() const
{
	return member.exclusions.size();
}
const BObject* BPicker::exclusion(int index) const
{
	return member.exclusions(index);
}
void BPicker::clearExclusions()
{
	member.exclusions.clear();
}

int BPicker::inclusionCount() const
{
	return member.inclusions.size();
}
const BObject* BPicker::inclusion(int index) const
{
	return member.inclusions(index);
}
void BPicker::clearInclusions()
{
	member.inclusions.clear();
}

BPickHit* BPicker::apply(BScene* scene)
{
	member.scene = scene;
	member.group = 0;
	member.node = 0;
	member.shape = 0;
	member.pickHits.clear();
	if (scene)
	{
		this->reset();
		if (scene_member(scene)->sceneOctTree.empty())
		{
			for (int i = 0; i < scene->nodeCount(); i++)
			{
				BNode* node = scene->node(i);
				member.enter(member.line, node);
			}
			for (int i = 0; i < scene->shapeCount(); i++)
			{
				BShape* shape = scene->shape(i);
				member.enter(member.line, shape);
			}
			for (int i = 0; i < scene->groupCount(); i++)
			{
				BGroup* group = scene->group(i);
				member.enter(member.line, group);
			}
		}
		else
		{
			member.sceneOctItems.clear();
			scene_member(scene)->sceneOctTree->intersect(member.line, member.sceneOctItems);
			for (int i = 0; i < member.sceneOctItems.size(); i++)
			{
				SceneSet& sceneSet = *member.sceneOctItems[i];
				for (int i = 0; i < sceneSet.shapes.size(); i++)
				{
					BShape* shape = sceneSet.shapes[i];
					member.enter(member.line, shape);
				}
				for (int i = 0; i < sceneSet.nodes.size(); i++)
				{
					BNode* node = sceneSet.nodes[i];
					member.enter(member.line, node);
				}
			}
			member.sceneOctItems.reserve(member.sceneOctItems.size());
		}
		this->finish();
		return member.pickHits.min();
	}
	return 0;
}
BPickHit* BPicker::apply(BGroup* group)
{
	member.scene = 0;
	member.group = 0;
	member.node = 0;
	member.shape = 0;
	member.pickHits.clear();
	if (group)
	{
		this->reset();
		member.enter(member.line, group);
		this->finish();
		return member.pickHits.min();
	}
	return 0;
}
BPickHit* BPicker::apply(BNode* node)
{
	member.scene = 0;
	member.group = 0;
	member.node = 0;
	member.shape = 0;
	member.pickHits.clear();
	if (node)
	{
		this->reset();
		member.enter(member.line, node);
		this->finish();
		return member.pickHits.min();
	}
	return 0;
}
BPickHit* BPicker::apply(BShape* shape)
{
	member.scene = 0;
	member.group = 0;
	member.node = 0;
	member.shape = 0;
	member.pickHits.clear();
	if (shape)
	{
		this->reset();
		member.enter(member.line, shape);
		this->finish();
		return member.pickHits.min();
	}
	return 0;
}

int BPicker::hitCount() const
{
	return member.pickHits.size();
}
BPickHit* BPicker::hit(int index)
{
	return member.pickHits(index);
}
const BPickHit* BPicker::hit(int index) const
{
	return member.pickHits(index);
}

void BPicker::clear()
{
	member.scene = 0;
	member.group = 0;
	member.node = 0;
	member.shape = 0;
	member.pickHits.clear();
}

bool BPicker::ignore(const BShape* shape) const
{
	if (member.inclusions.contain(shape))
		return false;
	if (!shape->visible())
		return true;
	return member.exclusions.contain(shape);
}
bool BPicker::ignore(const BNode* node) const
{
	if (member.inclusions.contain(node))
		return false;
	if (!node->visible())
		return true;
	return member.exclusions.contain(node);
}
bool BPicker::ignore(const BGroup* group) const
{
	if (member.inclusions.contain(group))
		return false;
	if (!group->visible())
		return true;
	return member.exclusions.contain(group);
}
bool BPicker::ignore(const BElement* element) const
{
	if (member.inclusions.contain(element))
		return false;
	if (!element->visible())
		return true;
	return member.exclusions.contain(element);
}

bool BPicker::record(BPickHit* hit)
{
	if (hit)
	{
		if (!hit_member(hit)->scene)
			hit_member(hit)->scene = member.scene;
		if (!hit_member(hit)->group)
			hit_member(hit)->group = member.group;
		if (!hit_member(hit)->node)
			hit_member(hit)->node = member.node;
		int pos = member.pickHits.size();
		for (int i = 0; i < member.pickHits.size(); i++)
		{
			if (member.pickHits[i]->ratio() > hit_member(hit)->ratio)
			{
				pos = i;
				break;
			}
		}
		member.pickHits.insert(pos, hit);
		if (member.pickHits.size() > member.hitNums)
		{
			member.pickHits.remove(member.hitNums);
		}
		return member.pickHits.contain(hit);
	}
	return false;
}
BPickHit* BPicker::create() const
{
	BPickHit* hit = new BPickHit();
	hit_member(hit)->scene = member.scene;
	hit_member(hit)->group = member.group;
	hit_member(hit)->node = member.node;
	hit_member(hit)->shape = member.shape;
	return hit;
}
BMatrix BPicker::matrix() const
{
	BMatrix matrix;
	if (member.shape)
		matrix = member.shape->matrix();
	if (member.node)
		matrix *= member.node->matrix();
	return matrix;
}

void BPicker::reset()
{

}
void BPicker::finish()
{

}

void BPicker::pick(const BLine& line, BGroup* group)
{
	for (int i = 0; i < group->shapeCount(); i++)
	{
		BShape* shape = group->shape(i);
		member.enter(line, shape);
	}
	for (int i = 0; i < group->nodeCount(); i++)
	{
		BNode* node = group->node(i);
		member.enter(line, node);
	}
	for (int i = 0; i < group->childCount(); i++)
	{
		BGroup* child = group->child(i);
		member.enter(line, child);
	}
}
void BPicker::pick(const BLine& line, BNode* node)
{
	if (ShapeOctTree* shapeOctTree = node_member(node)->shapeSpaceTree)
	{
		shapeOctTree->intersect(line, member.shapeOctItems);
		for (int si = 0; si < member.shapeOctItems.size(); si++)
		{
			BShapeArray& shapes = *member.shapeOctItems[si];
			for (int i = 0; i < shapes.size(); i++)
			{
				BShape* shape = shapes(i);
				if (this->ignore(shape))
					continue;
				if (shape_member(shape)->isolate)
					member.enter(member.line, shape);
				else
					member.enter(line, shape);
			}
		}
	}
	else
	{
		for (int i = 0; i < node->shapeCount(); i++)
		{
			BShape* shape = node->shape(i);
			if (this->ignore(shape))
				continue;
			if (shape_member(shape)->isolate)
				member.enter(member.line, shape);
			else
				member.enter(line, shape);
		}
	}
}
void BPicker::pick(const BLine& line, BShape* shape)
{
	if (BSphere* sphere = dynamic_cast<BSphere*>(shape))
	{
		this->pick(line, sphere);
		return;
	}
	if (BGeoSphere* geosphere = dynamic_cast<BGeoSphere*>(shape))
	{
		this->pick(line, geosphere);
		return;
	}
	if (BCube* cube = dynamic_cast<BCube*>(shape))
	{
		this->pick(line, cube);
		return;
	}
	if (BCone* cone = dynamic_cast<BCone*>(shape))
	{
		this->pick(line, cone);
		return;
	}
	if (BCylinder* cylinder = dynamic_cast<BCylinder*>(shape))
	{
		this->pick(line, cylinder);
		return;
	}
	if (BCapsule* capsule = dynamic_cast<BCapsule*>(shape))
	{
		this->pick(line, capsule);
		return;
	}
	if (BTube* tube = dynamic_cast<BTube*>(shape))
	{
		this->pick(line, tube);
		return;
	}
	if (BTorus* torus = dynamic_cast<BTorus*>(shape))
	{
		this->pick(line, torus);
		return;
	}
	if (BMesh* mesh = dynamic_cast<BMesh*>(shape))
	{
		this->pick(line, mesh);
		return;
	}
	if (BGeometry* geometry = dynamic_cast<BGeometry*>(shape))
	{
		this->pick(line, geometry);
		return;
	}
	if (BBillboard* billboard = dynamic_cast<BBillboard*>(shape))
	{
		this->pick(line, billboard);
		return;
	}
	if (BTerrain* terrain = dynamic_cast<BTerrain*>(shape))
	{
		this->pick(line, terrain);
		return;
	}
	if (BSurface* surface = dynamic_cast<BSurface*>(shape))
	{
		this->pick(line, surface);
		return;
	}
	if (BGridPlane* gridplane = dynamic_cast<BGridPlane*>(shape))
	{
		this->pick(line, gridplane);
		return;
	}
}
void BPicker::pick(const BLine& line, BMesh* mesh)
{
	BVectorArray* vertices = mesh->vertices();
	if (!vertices)
		return;
	bool cullback = mesh->cullback();
	if (mesh_member(mesh)->faceOctTree)
	{
		FaceInfoOctTree* faceOctTree = mesh_member(mesh)->faceOctTree;
		faceOctTree->intersect(line, member.faceOctItems);
		for (int fi = 0; fi < member.faceOctItems.size(); fi++)
		{
			const FaceInfoArray& faces = *member.faceOctItems[fi];
			for (int i = 0; i < faces.size(); i++)
			{
				const FaceInfo& finfo = faces[i];
				const BVector& va = (*vertices)[finfo.face.a()];
				const BVector& vb = (*vertices)[finfo.face.b()];
				const BVector& vc = (*vertices)[finfo.face.c()];
				BReal ratio = 0;
				if (line.intersect(va, vb, vc, ratio, cullback))
				{
					BVector normal = (vb - va).cross(vc - va);
					normal.normalize();
					BPickHit* hit = new BPickHit(mesh);
					hit->setPrimitive(Primitive_Triangles);
					hit->setRatio(ratio);
					hit->setIndex(i);
					hit->setPoint(line.vertex(ratio));
					hit->setNormal(normal);
					record(hit);
				}
			}
		}
	}
	else
	{
		if (BFaceArray* faces = mesh->faces())
		{
			for (int i = 0; i < faces->size(); i++)
			{
				const BFace& face = (*faces)[i];
				const BVector& va = (*vertices)[face.a()];
				const BVector& vb = (*vertices)[face.b()];
				const BVector& vc = (*vertices)[face.c()];
				BReal ratio = 0;
				if (line.intersect(va, vb, vc, ratio, cullback))
				{
					BVector normal = (vb - va).cross(vc - va);
					normal.normalize();
					BPickHit* hit = new BPickHit(mesh);
					hit->setPrimitive(Primitive_Triangles);
					hit->setRatio(ratio);
					hit->setIndex(i);
					hit->setPoint(line.vertex(ratio));
					hit->setNormal(normal);
					record(hit);
				}
			}
		}
		else
		{
			int last = vertices->size() - 3;
			for (int i = 0; i <= last; i += 3)
			{
				const BVector& va = (*vertices)[i];
				const BVector& vb = (*vertices)[i + 1];
				const BVector& vc = (*vertices)[i + 2];
				BReal ratio = 0;
				if (line.intersect(va, vb, vc, ratio, cullback))
				{
					BVector normal = (vb - va).cross(vc - va);
					normal.normalize();
					BPickHit* hit = new BPickHit(mesh);
					hit->setPrimitive(Primitive_Triangles);
					hit->setRatio(ratio);
					hit->setIndex(i / 3);
					hit->setPoint(line.vertex(ratio));
					hit->setNormal(normal);
					record(hit);
				}
			}
		}
	}
}
void BPicker::pick(const BLine& line, BGeometry* geometry)
{
	BVectorArray* vertices = geometry->vertices();
	if (!vertices || vertices->empty())
		return;
	bool cullback = geometry->cullback();
	if (geometry->optimized())
	{
		PolyOctTree* polyTree = geometry_member(geometry)->polyOctTree;
		polyTree->intersect(member.line, member.polyOctItems);
		for (int i = 0; i < member.polyOctItems.size(); i++)
		{
			PolyCell& polyCell = *member.polyOctItems[i];
			for (int i = 0; i < polyCell.triangles.size(); i++)
			{
				BReal ratio = 0;
				BTriangle& triangle = polyCell.triangles[i];
				if (line.intersect(triangle, ratio, cullback))
				{
					BPickHit* hit = new BPickHit(geometry);
					hit->setPrimitive(Primitive_Triangles);
					hit->setRatio(ratio);
					hit->setPoint(line.vertex(ratio));
					hit->setNormal(triangle.normal());
					hit->setElement(polyCell.element);
					hit->setIndex(i);
					record(hit);
				}
			}
			for (int i = 0; i < polyCell.quads.size(); i++)
			{
				BReal ratio = 0;
				BQuad& quad = polyCell.quads[i];
				if (line.intersect(quad, ratio, cullback))
				{
					BPickHit* hit = new BPickHit(geometry);
					hit->setPrimitive(Primitive_Quads);
					hit->setRatio(ratio);
					hit->setPoint(line.vertex(ratio));
					hit->setNormal(quad.normal());
					hit->setElement(polyCell.element);
					hit->setIndex(i);
					record(hit);
				}
			}
			for (int i = 0; i < polyCell.polygons.size(); i++)
			{
				BPolygon& polygon = polyCell.polygons[i];

			}
		}
	}
	else
	{
		for (int i = 0; i < geometry->elementCount(); i++)
		{
			BElement* element = geometry->element(i);
			if (this->ignore(element))
				continue;
			if (!line.intersect(element_member(element)->box, cullback))
				continue;
			Primitive primitive = element->primitive();
			if (BIntArray* indices = element->indices())
			{
				int size = indices->size();
				if (element->primitive() == Primitive_Triangles)
				{
					int last = indices->size() - 3;
					for (int i = 0; i <= last; i += 3)
					{
						int ia = (*indices)[i];
						int ib = (*indices)[i + 1];
						int ic = (*indices)[i + 2];
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						BReal ratio;
						if (line.intersect(va, vb, vc, ratio, cullback))
						{
							BVector normal = (vb - va).cross(vc - va);
							normal.normalize();
							BPickHit* hit = new BPickHit(geometry);
							hit->setPrimitive(Primitive_Triangles);
							hit->setRatio(ratio);
							hit->setPoint(line.vertex(ratio));
							hit->setNormal(normal);
							hit->setElement(element);
							hit->setIndex(i);
							record(hit);
						}
					}
					continue;
				}
				if (element->primitive() == Primitive_TriangleStrip)
				{
					for (int i = 0; i < size; i++)
					{
						int ia = (*indices)[i];
						int ib = (*indices)[i + 1];
						int ic = (*indices)[i + 2];
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						BReal ratio = 0;
						if (line.intersect(va, vb, vc, ratio, cullback))
						{
							BVector normal = (vb - va).cross(vc - va);
							normal.normalize();
							BPickHit* hit = new BPickHit(geometry);
							hit->setPrimitive(Primitive_Triangles);
							hit->setRatio(ratio);
							hit->setPoint(line.vertex(ratio));
							hit->setNormal(normal);
							hit->setElement(element);
							hit->setIndex(i);
							this->record(hit);
						}
					}
					continue;
				}
				if (element->primitive() == Primitive_Quads)
				{
					int last = size - 4;
					for (int i = 0; i <= last; i += 4)
					{
						int ia = (*indices)[i];
						int ib = (*indices)[i + 1];
						int ic = (*indices)[i + 2];
						int id = (*indices)[i + 3];
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						const BVector& vd = (*vertices)[id];
						BQuad quad(va, vb, vc, vd);
						BReal ratio = 0;
						if (line.intersect(quad, ratio, cullback))
						{
							BPickHit* hit = new BPickHit(geometry);
							hit->setPrimitive(Primitive_Quads);
							hit->setRatio(ratio);
							hit->setPoint(line.vertex(ratio));
							hit->setNormal(quad.normal());
							hit->setElement(element);
							hit->setIndex(i);
							this->record(hit);
						}
					}
					continue;
				}
				if (element->primitive() == Primitive_QuadStrip)
				{
					int last = size - 2;
					for (int i = 0; i <= last; i += 2)
					{
						int ia = (*indices)[i];
						int ib = (*indices)[i + 1];
						int ic = (*indices)[i + 3];
						int id = (*indices)[i + 2];
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						const BVector& vd = (*vertices)[id];
						BQuad quad(va, vb, vc, vd);
						BReal ratio = 0;
						if (line.intersect(quad, ratio, cullback))
						{
							BPickHit* hit = new BPickHit(geometry);
							hit->setPrimitive(Primitive_Quads);
							hit->setRatio(ratio);
							hit->setPoint(line.vertex(ratio));
							hit->setNormal(quad.normal());
							hit->setElement(element);
							hit->setIndex(i);
							this->record(hit);
						}
					}
					continue;
				}
			}
			if (const BPieceArray* pieces = element->pieces())
			{
				if (element->primitive() == Primitive_Triangles)
				{
					int last = pieces->size() - 3;
					for (int i = 0; i <= last; i += 3)
					{
						int ia = (*pieces)[i].v();
						int ib = (*pieces)[i + 1].v();
						int ic = (*pieces)[i + 2].v();
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						BReal ratio = 0;
						if (line.intersect(va, vb, vc, ratio, cullback))
						{
							BVector normal = (vb - va).cross(vc - va);
							normal.normalize();
							BPickHit* hit = new BPickHit(geometry);
							hit->setPrimitive(Primitive_Triangles);
							hit->setRatio(ratio);
							hit->setPoint(line.vertex(ratio));
							hit->setNormal(normal);
							hit->setElement(element);
							hit->setIndex(i);
							this->record(hit);
						}
					}
					continue;
				}
				if (element->primitive() == Primitive_TriangleStrip)
				{
					for (int i = 0; i < pieces->size(); i++)
					{
						int ia = (*pieces)[i].v();
						int ib = (*pieces)[i + 1].v();
						int ic = (*pieces)[i + 2].v();
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						BReal ratio = 0;
						if (line.intersect(va, vb, vc, ratio, cullback))
						{
							BVector normal = (vb - va).cross(vc - va);
							normal.normalize();
							BPickHit* hit = new BPickHit(geometry);
							hit->setPrimitive(Primitive_TriangleStrip);
							hit->setRatio(ratio);
							hit->setPoint(line.vertex(ratio));
							hit->setNormal(normal);
							hit->setElement(element);
							hit->setIndex(i);
							this->record(hit);
						}
					}
					continue;
				}
				if (element->primitive() == Primitive_Quads)
				{
					int last = pieces->size() - 4;
					for (int i = 0; i <= last; i += 4)
					{
						int ia = (*pieces)[i].v();
						int ib = (*pieces)[i + 1].v();
						int ic = (*pieces)[i + 2].v();
						int id = (*pieces)[i + 3].v();
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						const BVector& vd = (*vertices)[id];
						BQuad quad(va, vb, vc, vd);
						BReal ratio;
						if (line.intersect(quad, ratio, cullback))
						{
							BPickHit* hit = new BPickHit(geometry);
							hit->setPrimitive(Primitive_Quads);
							hit->setRatio(ratio);
							hit->setPoint(line.vertex(ratio));
							hit->setNormal(quad.normal());
							hit->setElement(element);
							hit->setIndex(i);
							this->record(hit);
						}
					}
					continue;
				}
				if (element->primitive() == Primitive_QuadStrip)
				{
					int last = pieces->size() - 2;
					for (int i = 0; i <= last; i += 2)
					{
						int ia = (*pieces)[i].v();
						int ib = (*pieces)[i + 1].v();
						int ic = (*pieces)[i + 3].v();
						int id = (*pieces)[i + 2].v();
						const BVector& va = (*vertices)[ia];
						const BVector& vb = (*vertices)[ib];
						const BVector& vc = (*vertices)[ic];
						const BVector& vd = (*vertices)[id];
						BQuad quad(va, vb, vc, vd);
						BReal ratio;
						if (line.intersect(quad, ratio, cullback))
						{
							BPickHit* hit = new BPickHit(geometry);
							hit->setPrimitive(Primitive_Quads);
							hit->setRatio(ratio);
							hit->setPoint(line.vertex(ratio));
							hit->setNormal(quad.normal());
							hit->setElement(element);
							hit->setIndex(i);
							this->record(hit);
						}
					}
					continue;
				}
				continue;
			}
			int first = element->first();
			int count = element->count();
			if (element->primitive() == Primitive_Triangles)
			{
				int last = count - 3;
				for (int i = first; i <= last; i += 3)
				{
					const BVector& va = (*vertices)[i];
					const BVector& vb = (*vertices)[i + 1];
					const BVector& vc = (*vertices)[i + 2];
					BReal ratio = 0;
					if (line.intersect(va, vb, vc, ratio, cullback))
					{
						BVector normal = (vb - va).cross(vc - va);
						normal.normalize();
						BPickHit* hit = new BPickHit(geometry);
						hit->setPrimitive(Primitive_Triangles);
						hit->setRatio(ratio);
						hit->setPoint(line.vertex(ratio));
						hit->setNormal(normal);
						hit->setElement(element);
						hit->setIndex(i);
						this->record(hit);
					}
				}
				continue;
			}
			if (element->primitive() == Primitive_TriangleStrip)
			{
				for (int i = first; i < count; i++)
				{
					const BVector& va = (*vertices)[i];
					const BVector& vb = (*vertices)[i + 1];
					const BVector& vc = (*vertices)[i + 2];
					BReal ratio = 0;
					if (line.intersect(va, vb, vc, ratio, cullback))
					{
						BVector normal = (vb - va).cross(vc - va);
						normal.normalize();
						BPickHit* hit = new BPickHit(geometry);
						hit->setPrimitive(Primitive_Triangles);
						hit->setRatio(ratio);
						hit->setPoint(line.vertex(ratio));
						hit->setNormal(normal);
						hit->setElement(element);
						hit->setIndex(i);
						this->record(hit);
					}
				}
				continue;
			}
			if (element->primitive() == Primitive_Quads)
			{
				int last = count - 4;
				for (int i = first; i <= last; i += 4)
				{
					const BVector& va = (*vertices)[i];
					const BVector& vb = (*vertices)[i + 1];
					const BVector& vc = (*vertices)[i + 2];
					const BVector& vd = (*vertices)[i + 3];
					BQuad quad(va, vb, vc, vd);
					BReal ratio = 0;
					if (line.intersect(quad, ratio, cullback))
					{
						BPickHit* hit = new BPickHit(geometry);
						hit->setPrimitive(Primitive_Quads);
						hit->setElement(element);
						hit->setRatio(ratio);
						hit->setPoint(line.vertex(ratio));
						hit->setNormal(quad.normal());
						hit->setIndex(i);
						record(hit);
					}
				}
				continue;
			}
			if (element->primitive() == Primitive_QuadStrip)
			{
				int last = count - 2;
				for (int i = first; i <= last; i += 2)
				{
					const BVector& va = (*vertices)[i];
					const BVector& vb = (*vertices)[i + 1];
					const BVector& vc = (*vertices)[i + 3];
					const BVector& vd = (*vertices)[i + 2];
					BQuad quad(va, vb, vc, vd);
					BReal ratio = 0;
					if (line.intersect(quad, ratio, cullback))
					{
						BPickHit* hit = new BPickHit(geometry);
						hit->setPrimitive(Primitive_Quads);
						hit->setElement(element);
						hit->setRatio(ratio);
						hit->setPoint(line.vertex(ratio));
						hit->setNormal(quad.normal());
						hit->setIndex(i);
						record(hit);
					}
				}
				continue;
			}
		}
	}
}
void BPicker::pick(const BLine& line, BSphere* sphere)
{
	BVector center(0, 0, 0);
	BVector vec = center - line.begin();
	BReal len = vec.dot(line.direct());
	if (len < 0)
		return;
	if (len > line.length())
		return;
	BVector foot = line.foot(center);
	BReal radius = sphere->radius();
	BReal dist0 = (foot - center).length();
	BReal dist = line.distance(center);
	if (dist < radius)
	{
		BReal len = sqrt(radius * radius - dist * dist);
		BVector point = foot - line.direct() * len;
		BReal ratio = (point - line.begin()).length() / line.length();
		BVector normal = point - center;
		normal.normalize();
		BPickHit* hit = new BPickHit();
		hit->setRatio(ratio);
		hit->setShape(sphere);
		hit->setPoint(point);
		hit->setNormal(normal);
		this->record(hit);
		return;
	}
}
void BPicker::pick(const BLine& line, BGeoSphere* geosphere)
{
	BVector foot = line.foot(BVector());
	BReal radius = geosphere->radius();
	BReal dist = foot.length();
	if (dist < radius)
	{
		BReal len = sqrt(radius * radius - dist * dist);
		BVector point = foot - line.direct() * len;
		BReal ratio = (point - line.begin()).length() / line.length();
		BVector normal = point;
		normal.normalize();
		BPickHit* hit = new BPickHit();
		hit->setRatio(ratio);
		hit->setShape(geosphere);
		hit->setPoint(point);
		hit->setNormal(normal);
		this->record(hit);
		return;
	}
}
void BPicker::pick(const BLine& line, BCylinder* cylinder)
{
	BReal height = cylinder->height();
	BReal radius = cylinder->radius();

	const BVector& begin = line.begin();
	const BVector& end = line.end();
	BReal dx = end.x() - begin.x();
	BReal dy = end.y() - begin.y();
	BReal dz = end.z() - begin.z();

	BReal half = height / 2;

	BReal A = dx * dx + dy * dy;
	if (std::abs(A) > REAL_EPSILON)
	{
		BReal B = 2 * (dx * begin.x() + dy * begin.y());
		BReal C = begin.x() * begin.x() + begin.y() * begin.y() - radius * radius;

		BReal discriminant = B * B - 4 * A * C;
		if (discriminant >= 0)
		{
			BReal t1 = (-B + std::sqrt(discriminant)) / (2 * A);
			BReal t2 = (-B - std::sqrt(discriminant)) / (2 * A);
			BReal ratio = bMin(t1, t2);
			if (ratio >= 0 && ratio <= 1)
			{
				BVector point = line.vertex(ratio);
				if (point.z() >= -half && point.z() <= half)
				{
					BPickHit* hit = new BPickHit(cylinder);
					hit->setRatio(ratio);
					hit->setPoint(point);
					point.z() = 0;
					hit->setNormal(point.normal());
					this->record(hit);
				}
			}
		}
	}

	if (std::abs(dz) > REAL_EPSILON)
	{
		BReal t_top = (half - begin.z()) / dz;
		if (t_top >= 0 && t_top <= 1)
		{
			BVector point(begin.x() + t_top * dx, begin.y() + t_top * dy, half);
			BReal dist = std::sqrt(point.x() * point.x() + point.y() * point.y());
			if (dist <= radius)
			{
				BPickHit* hit = new BPickHit(cylinder);
				hit->setRatio(t_top);
				hit->setPoint(point);
				hit->setNormal(BVector(0, 0, 1));
				this->record(hit);
			}
		}

		BReal t_bottom = (-half - begin.z()) / dz;
		if (t_bottom >= 0 && t_bottom <= 1)
		{
			BVector point(begin.x() + t_bottom * dx, begin.y() + t_bottom * dy, half);
			BReal dist = std::sqrt(point.x() * point.x() + point.y() * point.y());
			if (dist <= radius + REAL_EPSILON)
			{
				BPickHit* hit = new BPickHit(cylinder);
				hit->setRatio(t_bottom);
				hit->setPoint(point);
				hit->setNormal(BVector(0, 0, -1));
				this->record(hit);
			}
		}
	}
}
void BPicker::pick(const BLine& line, BCapsule* capsule)
{
	const BVector& begin = line.begin();
	const BVector& end = line.end();
	BVector dir = end - begin;
	BReal height = capsule->height();
	BReal radius = capsule->radius();
	BReal half = height / 2;

	BVector topCenter(0, 0, half);
	BVector bottomCenter(0, 0, -half);

	{
		BVector vec = begin - topCenter;

		BReal a = dir.dot(dir);
		BReal b = 2 * vec.dot(dir);
		BReal c = vec.dot(vec) - radius * radius;

		BReal discriminant = b * b - 4 * a * c;
		if (discriminant > 0)
		{
			discriminant = std::sqrt(discriminant);
			BReal t1 = (-b - discriminant) / (2 * a);
			BReal t2 = (-b + discriminant) / (2 * a);
			if (t1 >= 0 && t1 <= 1)
			{
				BVector point = begin + dir * t1;
				if (point.z() >= topCenter.z())
				{
					BPickHit* hit = new BPickHit(capsule);
					hit->setRatio(t1);
					hit->setPoint(point);
					hit->setNormal((point - topCenter).normal());
					this->record(hit);
				}
			}
			if (t2 >= 0 && t2 <= 1)
			{
				BVector point = begin + dir * t2;
				if (point.z() >= topCenter.z())
				{
					BPickHit* hit = new BPickHit(capsule);
					hit->setRatio(t1);
					hit->setPoint(point);
					hit->setNormal((point - topCenter).normal());
					this->record(hit);
				}
			}
		}
	}

	{
		BVector vec = begin - bottomCenter;

		BReal a = dir.dot(dir);
		BReal b = 2 * vec.dot(dir);
		BReal c = vec.dot(vec) - radius * radius;
		BReal discriminant = b * b - 4 * a * c;
		if (discriminant > 0)
		{
			discriminant = std::sqrt(discriminant);
			BReal t1 = (-b - discriminant) / (2 * a);
			BReal t2 = (-b + discriminant) / (2 * a);
			if (t1 >= 0 && t1 <= 1)
			{
				BVector point = begin + dir * t1;
				if (point.z() <= bottomCenter.z())
				{
					BPickHit* hit = new BPickHit(capsule);
					hit->setRatio(t1);
					hit->setPoint(point);
					hit->setNormal((point - bottomCenter).normal());
					this->record(hit);
				}
			}
			if (t2 >= 0 && t2 <= 1)
			{
				BVector point = begin + dir * t2;
				if (point.z() <= bottomCenter.z())
				{
					BPickHit* hit = new BPickHit(capsule);
					hit->setRatio(t1);
					hit->setPoint(point);
					hit->setNormal((point - bottomCenter).normal());
					this->record(hit);
				}
			}
		}
	}

	BVector dirXY(dir.x(), dir.y(), 0);
	BVector toCylinderXY(begin.x(), begin.y(), 0);

	BReal a = dirXY.dot(dirXY);
	BReal b = 2 * toCylinderXY.dot(dirXY);
	BReal c = toCylinderXY.dot(toCylinderXY) - radius * radius;

	BReal discriminant = b * b - 4 * a * c;
	if (discriminant > 0)
	{
		discriminant = std::sqrt(discriminant);
		BReal t1 = (-b - discriminant) / (2 * a);
		BReal t2 = (-b + discriminant) / (2 * a);
		BReal ratio = bMin(t1, t2);
		if (ratio >= 0 && ratio <= 1)
		{
			BVector point = line.vertex(ratio);
			if (point.z() >= -half && point.z() <= half)
			{
				BPickHit* hit = new BPickHit(capsule);
				hit->setRatio(ratio);
				hit->setPoint(point);
				point.z() = 0;
				hit->setNormal(point.normal());
				this->record(hit);
			}
		}
	}
}
void BPicker::pick(const BLine& line, BCube* cube)
{
	bool cullback = cube->cullback();
	BReal halfX = cube->length() / 2;
	BReal halfY = cube->width() / 2;
	BReal halfZ = cube->height() / 2;
	BVector min(-halfX, -halfY, -halfZ);
	BVector max(halfX, halfY, halfZ);

	const BVector v0(min.x(), min.y(), min.z());
	const BVector v1(max.x(), min.y(), min.z());
	const BVector v2(max.x(), max.y(), min.z());
	const BVector v3(min.x(), max.y(), min.z());

	const BVector v4(min.x(), min.y(), max.z());
	const BVector v5(max.x(), min.y(), max.z());
	const BVector v6(max.x(), max.y(), max.z());
	const BVector v7(min.x(), max.y(), max.z());

	BReal ratio = 0;
	BQuad bottom(v3, v2, v1, v0, BVector(0, 0, -1));
	if (line.intersect(bottom, ratio, cullback))
	{
		BPickHit* hit = new BPickHit(cube);
		hit->setRatio(ratio);
		hit->setPoint(line.vertex(ratio));
		hit->setNormal(bottom.normal());
		this->record(hit);
	}
	BQuad top(v4, v5, v6, v7, BVector(0, 0, 1));
	if (line.intersect(top, ratio, cullback))
	{
		BPickHit* hit = new BPickHit(cube);
		hit->setRatio(ratio);
		hit->setPoint(line.vertex(ratio));
		hit->setNormal(top.normal());
		this->record(hit);
	}
	BQuad left(v0, v1, v5, v4, BVector(-1, 0, 0));
	if (line.intersect(left, ratio, cullback))
	{
		BPickHit* hit = new BPickHit(cube);
		hit->setRatio(ratio);
		hit->setPoint(line.vertex(ratio));
		hit->setNormal(left.normal());
		this->record(hit);
	}
	BQuad back(v1, v2, v6, v5, BVector(0, -1, 0));
	if (line.intersect(back, ratio, cullback))
	{
		BPickHit* hit = new BPickHit(cube);
		hit->setRatio(ratio);
		hit->setPoint(line.vertex(ratio));
		hit->setNormal(back.normal());
		this->record(hit);
	}
	BQuad right(v2, v3, v7, v6, BVector(1, 0, 0));
	if (line.intersect(right, ratio, cullback))
	{
		BPickHit* hit = new BPickHit(cube);
		hit->setRatio(ratio);
		hit->setPoint(line.vertex(ratio));
		hit->setNormal(right.normal());
		this->record(hit);
	}
	BQuad front(v3, v0, v4, v7, BVector(0, 1, 0));
	if (line.intersect(front, ratio, cullback))
	{
		BPickHit* hit = new BPickHit(cube);
		hit->setRatio(ratio);
		hit->setPoint(line.vertex(ratio));
		hit->setNormal(front.normal());
		this->record(hit);
	}
}
void BPicker::pick(const BLine& line, BCone* cone)
{
	pick(line, (BGeometry*)cone);
}
void BPicker::pick(const BLine& line, BTube* tube)
{
	pick(line, (BGeometry*)tube);
}
void BPicker::pick(const BLine& line, BTorus* torus)
{
	pick(line, (BMesh*)torus);
}
void BPicker::pick(const BLine& line, BBillboard* billboard)
{
	const BQuad& quad = billboard_member(billboard)->facingQuad;
	BReal ratio = 0;
	if (line.intersect(quad, ratio))
	{
		BPickHit* hit = new BPickHit(billboard);
		hit->setRatio(ratio);
		hit->setPoint(line.vertex(ratio));
		hit->setNormal(quad.normal());
		record(hit);
		return;
	}
}
void BPicker::pick(const BLine& line, BTerrain* terrain)
{
	const BVectorArray* vertices = terrain->vertices();
	const BIntArray* indices = terrain->indices();
	if (!vertices || !indices)
		return;
	bool cullback = terrain->cullback();
	if (terrain_member(terrain)->quadTree)
	{
		QuadTree* quadTree = terrain_member(terrain)->quadTree;
		quadTree->intersect(line, member.quadItems);
		for (int qi = 0; qi < member.quadItems.size(); qi++)
		{
			BIntArray& indices = *member.quadItems[qi];
			int last = indices.size() - 4;
			for (int i = 0; i <= last; i += 4)
			{
				int ia = indices[i];
				int ib = indices[i + 1];
				int ic = indices[i + 2];
				int id = indices[i + 3];
				const BVector& va = (*vertices)[ia];
				const BVector& vb = (*vertices)[ib];
				const BVector& vc = (*vertices)[ic];
				const BVector& vd = (*vertices)[id];
				BQuad quad(va, vb, vc, vd);
				BReal ratio = 0;
				if (line.intersect(quad, ratio, cullback))
				{
					BPickHit* hit = new BPickHit(terrain);
					hit->setPrimitive(Primitive_Quads);
					hit->setRatio(ratio);
					hit->setPoint(line.vertex(ratio));
					hit->setNormal(quad.normal());
					hit->setIndex(i);
					this->record(hit);
				}
			}
		}
	}
	else
	{
		int last = indices->size() - 4;
		for (int i = 0; i <= last; i += 4)
		{
			int ia = (*indices)[i];
			int ib = (*indices)[i + 1];
			int ic = (*indices)[i + 2];
			int id = (*indices)[i + 3];
			const BVector& va = (*vertices)[ia];
			const BVector& vb = (*vertices)[ib];
			const BVector& vc = (*vertices)[ic];
			const BVector& vd = (*vertices)[id];
			BQuad quad(va, vb, vc, vd);
			BReal ratio = 0;
			if (line.intersect(quad, ratio, cullback))
			{
				BPickHit* hit = new BPickHit(terrain);
				hit->setPrimitive(Primitive_Quads);
				hit->setRatio(ratio);
				hit->setPoint(line.vertex(ratio));
				hit->setNormal(quad.normal());
				hit->setIndex(i);
				this->record(hit);
			}
		}
	}
}
void BPicker::pick(const BLine& line, BSurface* surface)
{
	const BVectorArray* vertices = surface->vertices();
	const BIntArray* indices = surface->indices();
	if (!vertices || !indices)
		return;
	bool cullback = surface->cullback();
	int last = indices->size() - 4;
	for (int i = 0; i <= last; i += 4)
	{
		int ia = (*indices)[i];
		int ib = (*indices)[i + 1];
		int ic = (*indices)[i + 2];
		int id = (*indices)[i + 3];
		const BVector& va = (*vertices)[ia];
		const BVector& vb = (*vertices)[ib];
		const BVector& vc = (*vertices)[ic];
		const BVector& vd = (*vertices)[id];
		BQuad quad(va, vb, vc, vd);
		BReal ratio = 0;
		if (line.intersect(quad, ratio, cullback))
		{
			BPickHit* hit = new BPickHit(surface);
			hit->setPrimitive(Primitive_Quads);
			hit->setRatio(ratio);
			hit->setPoint(line.vertex(ratio));
			hit->setNormal(quad.normal());
			hit->setIndex(i);
			this->record(hit);
		}
	}
}
void BPicker::pick(const BLine& line, BGridPlane* gridplane)
{
	BReal ratio = 0;
	if (line.intersect(BPlane(gridplane->normal(), gridplane->constant()), ratio))
	{
		BVector point = line.vertex(ratio);
		if (gridplane->box().contain(point))
		{
			BPickHit* hit = new BPickHit();
			hit->setRatio(ratio);
			hit->setShape(gridplane);
			hit->setPoint(point);
			hit->setNormal(gridplane->normal());
			this->record(hit);
		}
	}
}

